package com.quan.commons.aop;

import com.alibaba.fastjson.JSON;
import com.quan.commons.annotation.OperationLog;
import com.quan.commons.constant.CommonsConstant;
import com.quan.commons.interceptor.LoginUserInterceptor;
import com.quan.commons.log.LogVo;
import com.quan.commons.log.OperationLogPrinter;
import com.quan.commons.utils.JwtUtils;
import com.quan.commons.utils.LogHelper;
import com.quan.system.commons.utils.LoginUser;
import com.quan.system.commons.vo.SysUserVo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * 日志切面处理类
 */
@Slf4j
@Aspect
@Component
public class OperationLogAop {

    private OperationLogPrinter operationLogPrinter;

    public OperationLogAop(OperationLogPrinter operationLogPrinter) {
        this.operationLogPrinter = operationLogPrinter;
    }

    /**
     * 设置操作日志切入点   在注解的位置切入代码
     */
    @Pointcut("@annotation(com.quan.commons.annotation.OperationLog)")
    public void operationLogPointCut() {
    }

    /**
     * 记录操作日志
     *
     * @param point 方法的执行点
     * @throws Throwable
     */
    @Around("operationLogPointCut()")
    public Object saveOperationLog(ProceedingJoinPoint point) throws Throwable {
        Object result = null;
        long time = 0;
        try {
            long beginTime = System.currentTimeMillis();
            result = point.proceed();

            //执行时长(毫秒)
            time = System.currentTimeMillis() - beginTime;return result;
        } finally {
            try {
                // 获取方法参数
                String params = LogHelper.getMethodParam(point);

                MethodSignature signature = (MethodSignature) point.getSignature();
                Method method = signature.getMethod();
                OperationLog annotation = method.getAnnotation(OperationLog.class);

                if (annotation != null) {
                    String module = annotation.module();
                    String type = annotation.type();
                    String title = annotation.title();

                    //请求的方法名
                    String className = point.getTarget().getClass().getName();
                    String methodName = signature.getName();

                    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
                    HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

                    String token = request.getHeader("token");

                    if (methodName.startsWith("login")) {
                        // 用户登录无法从浏览器中获取token，需从执行登录方法的返回值中获取token
                        token = JSON.parseObject(JSON.toJSONString(result)).getString("token");
                        params = null;
                    }

                    if(null != token) {
                        Long userId = Long.parseLong(JwtUtils.getUserId(token));
                        String userName = JwtUtils.getUserName(token);

                        String ipAddr = this.getRemoteHost(request);

                        LogVo vo = new LogVo();
                        vo.setType(getOperateType(methodName, Integer.parseInt(type)));
                        vo.setUserId(userId);
                        vo.setUserName(userName);
                        vo.setIp(ipAddr);
                        vo.setTitle(title);
                        vo.setRequestUri(request.getRequestURI());
                        vo.setRequestMethod(request.getMethod());
                        vo.setRequestParams(params);
                        vo.setUserAgent(request.getHeader("User-Agent"));
                        vo.setExecTime(time);
                        vo.setExecMethod(className + "." + methodName + "()");

                        // 打印日志
                        operationLogPrinter.log(vo);
                    }

                }
            } catch (Exception e) {
                log.error("记录操作日志发生错误", e);
            }
        }
    }

    /**
     * 获取操作类型
     */
    private int getOperateType(String methodName,int operateType) {
        if (methodName.startsWith("login")) {
            return CommonsConstant.LogTypeEnums.LOGIN.getIndex();
        }
        if (methodName.startsWith("logout")) {
            return CommonsConstant.LogTypeEnums.LOGIN.getIndex();
        }

        // 其他的默认为业务操作日志
        return CommonsConstant.LogTypeEnums.BUSINESS.getIndex();
    }

    /**
     * 获取目标主机的ip
     *
     * @param request 请求
     */
    private String getRemoteHost(HttpServletRequest request) {
        if (request == null) {
            return "";
        }

        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }
}
